#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "typedefs.h"
#include "hal.h"
#include "phy.h"
#include "phy_tx.h"
#include "phy_rx.h"


PHY_tx_handle_t PHY_tx_handle_s;
PHY_tx_data_t   PHY_tx_data_s;
PHY_tx_scratchBuf_t  PHY_tx_scratchBuf;
SINT16 PHY_tx_prmb_workBuf[262];

extern UINT16 PHY_rxSuspend_done;
extern PHY_bandCfg_t PHY_bandCfg_s;



int16 PHY_txWndCoef[4]=
{
  0, 4797, 16384, 27971
};


const ComplexShort PHY_prmb_freq[49] =
{
	// Preamble in frequency domain, in Q15
	{    9672,    9028},
	{   14229,    8189},
	{   19602,    4800},
	{   24193,   -2490},
	{   24703,  -13951},
	{   16892,  -26631},
	{    -784,  -32758},
	{  -21122,  -22899},
	{  -26825,    2659},
	{   -5629,   22767},
	{   23479,    9899},
	{   17873,  -24119},
	{  -19482,  -22698},
	{  -18149,   17387},
	{   23609,   10632},
	{    5265,  -29773},
	{  -26494,    4242},
	{   20519,   15282},
	{     -48,  -30059},
	{  -18085,   17931},
	{   26392,   -9682},
	{  -26874,   -7907},
	{   23663,   11136},
	{  -20607,  -20575},
	{   19316,   16821},
	{  -20607,  -20575},
	{   23663,   11136},
	{  -26874,   -7906},
	{   26392,   -9682},
	{  -18085,   17932},
	{     -48,  -30058},
	{   20519,   15283},
	{  -26494,    4244},
	{    5265,  -29772},
	{   23609,   10634},
	{  -18149,   17389},
	{  -19482,  -22696},
	{   17873,  -24117},
	{   23479,    9902},
	{   -5629,   22770},
	{  -26825,    2662},
	{  -21122,  -22896},
	{    -784,  -32755},
	{   16892,  -26628},
	{   24703,  -13948},
	{   24193,   -2486},
	{   19602,    4803},
	{   14229,    8193},
	{    9672,    9032},
};

/* TX levels in 3dB step */
/* scale=32767*10^(dB/20) */
const UINT16 PHY_txLevScale[8] =
{
	20420,   //       0, -0dB
	14456,   //       1, -3dB
	10234,   //       2, -6dB
	7245,    //       3, -9dB
	5129,    //       4, -12dB
	3631,    //       5, -15dB
	2571,    //       6, -18dB
	1820     //       7, -21dB
};


SINT16 PHY_txUpsCoeff[64] =
{
	// Upsampling filter Q14
    103,    103,   1758,   1758,    285,    285,    -77,    -77,
    -26,    -26,   1673,   1673,    511,    511,    -89,    -89,
   -106,   -106,   1512,   1512,    768,    768,   -121,   -121,
   -142,   -142,   1292,   1292,   1036,   1036,   -143,   -143,
   -143,   -143,   1036,   1036,   1292,   1292,   -142,   -142,
   -121,   -121,    768,    768,   1512,   1512,   -106,   -106,
    -89,    -89,    511,    511,   1673,   1673,    -26,    -26,
    -77,    -77,    285,    285,   1758,   1758,    103,    103,
};

const UINT16 PHY_ppduMaxLen[14] =
{
	150, 
	150, 
	150, 
	0,   
	150, 
	150, 
	150, 
	0,   
	150,
	0,   
	0,   
	0,   
	150, 
	0    
};


/*****************************************************************************/
/* PTXM State Matrix                                                    */
/*****************************************************************************/
const FSM_actionCell_t PHY_txStateMatrix[NUM_PHY_TX_STATES][NUM_PHY_TX_EVENTS] =
{
	/* PHY_TX_IDLE */
	{
		{PHY_TX_START,       PHY_tx_start},			/* PHY_TX_EVT_START  -> start preamble */
		{PHY_TX_IDLE,        PHY_tx_nop},			/* PHY_TX_EVT_RETRY  -> no action */
		{PHY_TX_IDLE,        PHY_tx_nop},			/* PHY_TX_EVT_FAIL   -> no action */
		{PHY_TX_IDLE,        PHY_tx_nop},			/* PHY_TX_EVT_DONE   -> no action */
	},
	/* PHY_TX_START */
	{
		{PHY_TX_SYNCP,       PHY_tx_syncpStart},	/* PHY_TX_EVT_START  -> start preamble */
		{PHY_TX_IDLE,        PHY_tx_nop},			/* PHY_TX_EVT_RETRY  -> no action */
		{PHY_TX_IDLE,        PHY_tx_nop},			/* PHY_TX_EVT_FAIL   -> no action */
		{PHY_TX_IDLE,        PHY_tx_nop},			/* PHY_TX_EVT_DONE   -> no action */
	},
	/* SYNCP */
	{
		{PHY_TX_SYNCP,       PHY_tx_syncpProc},		/* PHY_TX_EVT_START  ->     */
		{PHY_TX_SYNCP,       PHY_tx_nop},			/* PHY_TX_EVT_RETRY  -> no action */
		{PHY_TX_IDLE,        PHY_tx_fail},			/* PHY_TX_EVT_FAIL   -> fail      */
		{PHY_TX_SYNCM,       PHY_tx_syncmStart},	/* PHY_TX_EVT_DONE   -> start syncm */
	},
	/* PHY_TX_SYNCM */
	{
		{PHY_TX_SYNCM,       PHY_tx_syncmProc},		/* PHY_TX_EVT_START  ->     */
		{PHY_TX_SYNCM,       PHY_tx_nop},			/* PHY_TX_EVT_RETRY  -> no action */
		{PHY_TX_IDLE,        PHY_tx_fail},			/* PHY_TX_EVT_FAIL   -> fail      */
		{PHY_TX_HDR,         PHY_tx_hdrStart},		/* PHY_TX_EVT_DONE   -> start header */
	},
	/* PHY_TX_HDR */
	{
		{PHY_TX_HDR,         PHY_tx_hdrProc},		/* PHY_TX_EVT_START  ->     */
		{PHY_TX_HDR,         PHY_tx_nop},			/* PHY_TX_EVT_RETRY  -> no action */
		{PHY_TX_IDLE,        PHY_tx_fail},			/* PHY_TX_EVT_FAIL   -> fail      */
		{PHY_TX_DATA,        PHY_tx_dataStart},		/* PHY_TX_EVT_DONE   -> start data */
	},
	/* PHY_TX_DATA */
	{
		{PHY_TX_DATA,       PHY_tx_dataProc},		/* PHY_TX_EVT_START  ->      */
		{PHY_TX_LAST,       PHY_tx_dataLast},       /* PHY_TX_EVT_RETRY  -> no action */
		{PHY_TX_IDLE,       PHY_tx_fail},			/* PHY_TX_EVT_FAIL   -> fail      */
		{PHY_TX_IDLE,       PHY_tx_complete},		/* PHY_TX_EVT_DONE   ->   */
	},
	/* PHY_TX_LAST */
	{
		{PHY_TX_IDLE,       PHY_tx_complete},		/* PHY_TX_EVT_START  ->      */
		{PHY_TX_IDLE,       PHY_tx_nop},			/* PHY_TX_EVT_RETRY  -> no action */
		{PHY_TX_IDLE,       PHY_tx_fail},			/* PHY_TX_EVT_FAIL   -> fail      */
		{PHY_TX_IDLE,       PHY_tx_nop},			/* PHY_TX_EVT_DONE   ->   */
	},
};

/*****************************************************************************/
/* State machine                                                             */
/*****************************************************************************/
FSM_stateMachine_t PHY_txSm =
{
	&(PHY_txStateMatrix[0][0]),
	NUM_PHY_TX_STATES,
	NUM_PHY_TX_EVENTS,
	PHY_TX_IDLE,
	PHY_TX_IDLE,
	0
};

PHY_status_t (*PHY_tx_set[NUM_PHY_TX_SETPARMS])(PHY_txSetData_t *setParms_p) =
{
	PHY_tx_setStatClr,
	PHY_tx_setRoboMode
};

/******************************************************************************
* FUNCTION NAME: PHY_txSet()
*
* DESCRIPTION:   This function sets TX parameters
*
* Return Value:
*
* Input Parameters:
*                  setType    - Set parameter type
*                  setParms_p - pointer to set parameters data structure
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_txSet(Uint16 setType, PHY_txSetData_t *setParms_p)
{
	if (setType < NUM_PHY_TX_SETPARMS)
	{
		return (*PHY_tx_set[setType])(setParms_p);
	}
	else
	{
		return PHY_STAT_FAILURE;
	}
}

/******************************************************************************
* FUNCTION NAME: PHY_tx_setStatClr()
*
* DESCRIPTION:   This function sets TX parameters
*
* Return Value:
*
* Input Parameters:
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_tx_setStatClr(PHY_txSetData_t *getParms_p)
{
	memset(&PHY_tx_data_s.stats, 0, sizeof(PHY_tx_stat_t));

	/* Default TX processing delay, from PHY_txPpdu() to preamble ready to send */
	PHY_tx_data_s.stats.phyTxProcDelay = 50;

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_tx_setRoboMode()
*
* DESCRIPTION:   This function sets TX Robo Mode
*
* Return Value:
*
* Input Parameters:
*                *setParms_p - 0: PRIME; 1: ROBO
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_tx_setRoboMode(PHY_txSetData_t *setParms_p)
{
	if (setParms_p->roboMode == 1)
	{
		PHY_tx_handle_s.roboMode = 1;
		PHY_tx_data_s.numHdrSymbols = 16;
	}
	else
	{
		PHY_tx_handle_s.roboMode = 0;
		PHY_tx_data_s.numHdrSymbols = 2;
	}

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_txInit()
*
* DESCRIPTION:   This function initialize TX data structures and HW
*
* Return Value:
*
* Input Parameters: None
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_txInit(void)
{
	PLC_print("[PHY_txInit]: -----\n");

	memset(&PHY_tx_handle_s, 0, sizeof(PHY_tx_handle_t));

	PHY_tx_handle_s.sm_p = &PHY_txSm;
	PHY_tx_handle_s.roboMode = 0;

	memset(&PHY_tx_data_s, 0, sizeof(PHY_tx_data_t));

	PHY_tx_data_s.bufBegin_p = (int16 *)(&PHY_afeBuf_s.PHY_tx_outBuf[1][0] + 1128) - 286;
	PHY_tx_data_s.bufUserBegin_p = PHY_tx_data_s.bufBegin_p + 3;

	PHY_tx_data_s.bufEnd_p = PHY_tx_data_s.bufBegin_p + 286 -1;

	PHY_tx_data_s.bufA_p = PHY_tx_data_s.bufUserBegin_p;
	PHY_tx_data_s.bufB_p = PHY_tx_data_s.bufA_p + (96 >> 1);

	PHY_tx_data_s.ifft_hnd.ipcbptr = PHY_tx_data_s.bufUserBegin_p + 12;
	PHY_tx_data_s.ifft_hnd.size = 128;
	PHY_tx_data_s.ifft_hnd.nrstage = 7;
	PHY_tx_data_s.ifft_hnd.step = 8;
	PHY_tx_data_s.ifft_hnd.init = PHY_cfft16_init;
	PHY_tx_data_s.ifft_hnd.calc = PHY_cfft16_calc;

	PHY_tx_data_s.ifft_hnd.init(&PHY_tx_data_s.ifft_hnd);

	PHY_tx_data_s.ifft_prmb_hnd.ipcbptr = &PHY_tx_prmb_workBuf[6];
	PHY_tx_data_s.ifft_prmb_hnd.size = 128;
	PHY_tx_data_s.ifft_prmb_hnd.nrstage = 7;
	PHY_tx_data_s.ifft_prmb_hnd.step = 8;
	PHY_tx_data_s.ifft_prmb_hnd.init = PHY_cfft16_init;
	PHY_tx_data_s.ifft_prmb_hnd.calc = PHY_cfft16_calc;

	PHY_tx_data_s.ifft_hnd.init(&PHY_tx_data_s.ifft_prmb_hnd);

	/* Setup other scratch pointers */
	PHY_tx_data_s.intlvOut_p =  PHY_tx_data_s.bufEnd_p - 48 + 1;
	PHY_tx_data_s.modOut_p = PHY_tx_data_s.ifft_hnd.ipcbptr + 172;
	PHY_tx_data_s.upsIn_p = PHY_tx_data_s.bufBegin_p;

	PHY_firInit(&PHY_tx_data_s.upsFir, PHY_txUpsCoeff, 32);

	/* Configure HAL AFE TX */
	HAL_afe_txSetParms_s.txBuf_p = (int16 *)&PHY_afeBuf_s.PHY_tx_outBuf[0][0];
	HAL_afe_txSetParms_s.txSize = 1128;

	HAL_afeSet(AFE_TX_CFG, &HAL_afe_txSetParms_s);

	PHY_tx_data_s.afeBuf_p[0] = (Uint16 *)(Uint16 *)&PHY_afeBuf_s.PHY_tx_outBuf[0][0];
	PHY_tx_data_s.afeBuf_p[1] = (Uint16 *)(Uint16 *)&PHY_afeBuf_s.PHY_tx_outBuf[1][0];
	PHY_tx_data_s.afeBufIdx = 0;

	/* Stop the AFE */
	HAL_afeSet(AFE_TX_STOP, NULL);

	/* Default TX processing delay, from PHY_txPpdu() to preamble ready to send */
	PHY_tx_data_s.stats.phyTxProcDelay = 50;
	PHY_tx_data_s.upsScale = 16383;

	/* Default PRIME mode */
	PHY_tx_data_s.numHdrSymbols = 2;
	PHY_tx_data_s.prmbScale = 15000 ;
	PHY_tx_data_s.txClip    = 28527;

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_tx_start
*
* DESCRIPTION:   This function starts a PPDU transmit state machine
*                If txTime=0, it directly transitions to preamble processing
*                Otherwise, transitions after txTime timeout
*
* Return Value:
*
* Input Parameters:
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_tx_start(void *data_p)
{
	PHY_tx_data_s.timeLog.startTimeLast = PHY_tx_data_s.timeLog.startTime;
	PHY_tx_data_s.timeLog.startTime = 10;

	PLC_print("[PHY_tx_start]: symbCnt = %d\n", PHY_tx_data_s.symbCnt);

	PHY_rxSuspend_done = 0;

	PHY_rxSuspend(PHY_rxSuspend_cb);

	if (PHY_tx_data_s.txTime == 0)
	{
		FSM_event(data_p, PHY_TX_EVT_START, (void *)data_p);
	}
	else
	{
		if (PHY_tx_data_s.txTime < PHY_tx_data_s.timeLog.startTime)
		{
			HAL_afe_txSetParms_s.t0PrdInUs = (PHY_tx_data_s.timeLog.startTime - PHY_tx_data_s.txTime) * 10;
		}
		else
		{
			HAL_afe_txSetParms_s.t0PrdInUs = (PHY_tx_data_s.timeLog.startTime + 0xFFFFF - PHY_tx_data_s.txTime) * 10;
		}

		HAL_afe_txSetParms_s.t0PrdInUs = HAL_afe_txSetParms_s.t0PrdInUs << 1;
		HAL_afe_txSetParms_s.t0Flags = HAL_AFE_TIMER_INT_ENABLE | HAL_AFE_TIMER_1_SHOT;
		HAL_afe_txSetParms_s.cb_p    = PHY_tx_handle_s.timerCb_p;

		HAL_afeSet(AFE_TX_T0CFG, &HAL_afe_txSetParms_s);

		HAL_afeSet(AFE_TX_T0START, &HAL_afe_txSetParms_s);
	}

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_tx_complete
*
* DESCRIPTION:   This function completes a PPDU transmission
*                It stops AFE TX and does necessary callbacks
*
* Return Value:
*
* Input Parameters:
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_tx_complete(void *data_p)
{
	PHY_cbData_t cbData;

	PLC_print("[PHY_tx_complete]: symbCnt = %d\n", PHY_tx_data_s.symbCnt);

	PHY_tx_data_s.timeLog.dataStopTime = 10;
	PHY_tx_data_s.stats.phyTxPacketCount++;

	PHY_tx_handle_s.ppduBusy = 0;
	PHY_tx_handle_s.ppduCnt++;

	HAL_afeSet(AFE_TX_STOP, NULL);

	cbData.cbParms.txPpdu.ppduAddr = (UINT32)PHY_tx_data_s.ppduOrig_p;
	cbData.status = PHY_STAT_SUCCESS;

	if (PHY_tx_data_s.cb_p)
	{
		PHY_tx_data_s.cb_p(PHY_EV_TX_PPDU_DONE, &cbData);
	}

	/* Now check if there is que'ed PPDU */
	if (PHY_tx_data_s.txQueue.txQLen != 0)
	{
		PHY_txPpdu(&PHY_tx_data_s.txQueue.txPpdu[PHY_tx_data_s.txQueue.rdPtr], PHY_tx_data_s.txQueue.cb_p[PHY_tx_data_s.txQueue.rdPtr]);

		PHY_tx_data_s.txQueue.rdPtr++;

		if (PHY_tx_data_s.txQueue.rdPtr >= 2)
		{
			PHY_tx_data_s.txQueue.rdPtr = 0;
		}

		PHY_tx_data_s.txQueue.txQLen--;
		PHY_tx_data_s.stats.phyTxQLen = PHY_tx_data_s.txQueue.txQLen;
	}
	else
	{
		/* Check if there is suspend command received during transmission */
		if (PHY_tx_data_s.txSuspend == 1)
		{
			if (PHY_tx_data_s.txSuspendCb_p != NULL)
			{
				PHY_tx_data_s.txSuspendCb_p(PHY_EV_TX_SUSPEND_DONE, 0);
			}
		}
	}

	/* Resume RX */
	PHY_rxResume( );

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_tx_nop
*
* DESCRIPTION:   This function is a dummy state function
*
* Return Value:
*
* Input Parameters:
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_tx_nop(void *data_p)
{
	PLC_print("[PHY_tx_nop]: symbCnt = %d\n", PHY_tx_data_s.symbCnt);

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_tx_fail
*
* DESCRIPTION:   This function does failure processing
*
* Return Value:
*
* Input Parameters:
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_tx_fail(void *data_p)
{
	PLC_print("[PHY_tx_fail]: symbCnt = %d\n", PHY_tx_data_s.symbCnt);

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_txSmRun()
*
* DESCRIPTION:   This function runs TX state machine
*
* Return Value:
*
* Input Parameters:
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_txSmRun(void)
{
	FSM_event(PHY_tx_handle_s.sm_p, PHY_TX_EVT_START, (void *)PHY_tx_handle_s.sm_p);

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_txPpdu()
*
* DESCRIPTION:   This function starts TX PPDU tranmission
*
* Return Value:
*
* Input Parameters:
*                  ppdu - pointer to PPDU parameter data structure
*                  cb_p - pointer to the call back function
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_txPpdu(PHY_tx_ppdu_t *ppdu, PHY_cbFunc_t cb_p)
{
	PLC_print("[PHY_txPpdu]: ppdu = 0x%x\n", ppdu);

	/* Check if PHY TX in suspend mode */
	if (PHY_tx_data_s.txSuspend == 1)
	{
		return PHY_STAT_ILLEGAL_OPERATIONS;
	}

	PHY_tx_handle_s.ppduStartCnt++;

	/* Check the input parameters */
	if (((ppdu->mcs > PRIME_PRCL_D8PSK_F) && (PHY_tx_handle_s.roboMode == 0)) ||
	    (((ppdu->mcs & 0x03) > PRIME_PRCL_D8PSK_F) && (PHY_tx_handle_s.roboMode == 1)) ||
	    (ppdu->mcs == PRIME_PRCL_RSVD))
	{
		/* Invalid mcs */
		return PHY_STAT_INVALID_SCH;
	}

	if ((((ppdu->mcs & 0x0C) >> 2) > 1) &&
	    ((ppdu->mcs & 0x03) != 0))
	{
		/* Invalid mcs */
		return PHY_STAT_INVALID_SCH;
	}

	if (ppdu->length > PHY_ppduMaxLen[ppdu->mcs])
	{
		/* Invalid length */
		return PHY_STAT_INVALID_LEN;
	}

	if (ppdu->level >= 8)
	{
		/* Invalid level */
		return PHY_STAT_INVALID_LEV;
	}

	PHY_tx_data_s.upsScale = PHY_txLevScale[ppdu->level];
	PHY_tx_data_s.timeLog.ppduTime = 10;

	/* txTime=0 is considered immediate start with default processing delay */
	if (ppdu->txTime != 0)
	{
		if ((ppdu->txTime > PHY_tx_data_s.timeLog.ppduTime) &&
			((PHY_tx_data_s.timeLog.ppduTime + 0xFFFFF - ppdu->txTime) > 1000000))
		{
			PHY_tx_data_s.stats.phyStatsTxDropCount++;
  
			return PHY_STAT_TX_LATE;
		}
	}

	if (PHY_tx_handle_s.ppduBusy != 0)
	{
		/* If TX is busy, put in the queue */
		/* Check if queue overflow */
		if (PHY_tx_data_s.txQueue.txQLen >= 2)
		{
			  PHY_tx_data_s.stats.phyStatsTxDropCount++;
  
			  return PHY_STAT_BUF_OVRUN;
		}

		PHY_tx_data_s.txQueue.txQLen++;
		PHY_tx_data_s.stats.phyTxQLen = PHY_tx_data_s.txQueue.txQLen;

		/* Queue is fine, store PPDU parameters */
		memcpy(&PHY_tx_data_s.txQueue.txPpdu[PHY_tx_data_s.txQueue.wrPtr], ppdu, sizeof(PHY_tx_ppdu_t));

		PHY_tx_data_s.txQueue.cb_p[PHY_tx_data_s.txQueue.wrPtr] = cb_p;
		PHY_tx_data_s.txQueue.wrPtr++;

		if (PHY_tx_data_s.txQueue.wrPtr >= 2)
		{
			PHY_tx_data_s.txQueue.wrPtr = 0;
		}

		return PHY_STAT_SUCCESS;
	}

	PHY_tx_handle_s.ppduBusy = 1;

	/* Update TX data structure from PPDU parameters */
	PHY_tx_data_s.cb_p = cb_p;
	PHY_tx_data_s.txTime = ppdu->txTime;
	PHY_tx_data_s.mod = ppdu->mcs & 0x03;

	if (PHY_tx_handle_s.roboMode == 0)
	{
		PHY_tx_data_s.enc = (ppdu->mcs & 0x04) >> 2;
	}
	else
	{
		PHY_tx_data_s.enc = (ppdu->mcs & 0x0C) >> 2;
	}

	PHY_tx_data_s.numInBits = ppdu->length << 3;

	/* In ROBO mode, if mcs=7, data symbols are robo-enabled, coding is CBPSK+repetition */
	if ((PHY_tx_handle_s.roboMode == 1) && (PHY_tx_data_s.enc > 1))
	{
		/* For 1/4, 3 uBytes (6 cBytes) per 2 symbols, or 12 uBits (24 cBits)/symbol;
		   For 1/8, 3 uBytes (6 cBytes) per 4 symbols, or 6 uBits (12 cBits)/symbol   */
		PHY_tx_data_s.numBitsInSymbol = 48 >> PHY_tx_data_s.enc;
	}
	else
	{
		PHY_tx_data_s.numBitsInSymbol = 48 * (PHY_tx_data_s.mod + 1);
	}

	if (PHY_tx_data_s.enc == 0)
	{
		PHY_tx_data_s.numDataSymbols = (PHY_tx_data_s.numInBits + PHY_tx_data_s.numBitsInSymbol - 1) / PHY_tx_data_s.numBitsInSymbol;
		PHY_tx_data_s.numCodedPldBits = PHY_tx_data_s.numDataSymbols * PHY_tx_data_s.numBitsInSymbol;
		PHY_tx_data_s.numPldBits = PHY_tx_data_s.numCodedPldBits;

		/* Padding bits */
		/* w/o coding, = Payload - NumInBits */
		PHY_tx_data_s.numPadBits = PHY_tx_data_s.numPldBits - PHY_tx_data_s.numInBits;
	}
	else
	{
		if ((PHY_tx_handle_s.roboMode == 1) && (PHY_tx_data_s.enc > 1))
		{
		  /* Note we are counting every 2 (4) symbols for 1/4 (1/8)
			 numBitsInSymbol in struct is conv coded bits = uBits in 2 symbols */
			PHY_tx_data_s.numDataSymbols = (((((PHY_tx_data_s.numInBits + 8)*2 + PHY_tx_data_s.numBitsInSymbol - 1) / PHY_tx_data_s.numBitsInSymbol) + 4 - 1) >> 2) << 2;
		}
		else
		{
			PHY_tx_data_s.numDataSymbols = ((PHY_tx_data_s.numInBits + 8) * 2 + PHY_tx_data_s.numBitsInSymbol - 1) / PHY_tx_data_s.numBitsInSymbol;
		}

		PHY_tx_data_s.numCodedPldBits = PHY_tx_data_s.numDataSymbols * PHY_tx_data_s.numBitsInSymbol;
		PHY_tx_data_s.numPldBits = PHY_tx_data_s.numCodedPldBits >> 1;

		/* Padding bits before coding */
		PHY_tx_data_s.numPadBits = PHY_tx_data_s.numPldBits - 8 - PHY_tx_data_s.numInBits;
	}

	if (PHY_tx_handle_s.roboMode == 1)
	{
		PHY_tx_data_s.cnvEnc.c_p = (Uint16 *)PHY_tx_scratchBuf.buf3;
	}

	PHY_tx_data_s.ppduHdr_p = ppdu->ppduHdr_p;
	PHY_tx_data_s.ppduPld_p = ppdu->ppduPld_p;
	PHY_tx_data_s.ppduOrig_p = ppdu->ppduPld_p;

	if (ppdu->ppduHdr_p == NULL)
	{
		return PHY_STAT_FAILURE;
	}

	/* Initialize PN sequence for scrambler & modulator */
	PHY_scm_pnLoad( );

	PHY_mod_pnLoad( );

	PHY_tx_data_s.numSamps = 1024;

	/* Configure TX size, etc. */
	PHY_tx_data_s.afeBufIdx = 0;

	PHY_tx_handle_s.txSize = 1 * 1024;

	HAL_afe_txSetParms_s.txBuf_p = (int16 *)PHY_tx_data_s.afeBuf_p[PHY_tx_data_s.afeBufIdx];
	HAL_afe_txSetParms_s.txSize = 1 * 1024;

	HAL_afeSet(AFE_TX_CFG, &HAL_afe_txSetParms_s);

	/* Start sm */
	FSM_event(PHY_tx_handle_s.sm_p, PHY_TX_EVT_START, (void *)PHY_tx_handle_s.sm_p);

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_tx_dataLast
*
* DESCRIPTION:   This function is a transition for the last data symbol
*                The processing of the last symbols has already finished
*
* Return Value:
*
* Input Parameters:
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_tx_dataLast(void *data_p)
{
	PLC_print("[PHY_tx_dataLast]: symbCnt = %d\n", PHY_tx_data_s.symbCnt);

	PHY_tx_data_s.symbCnt++;

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_txCpInsert
*
* DESCRIPTION:   Insert PHY TX CP
*
* Return Value:  None
*
* Input Parameters: *cp_p -- pointer to the first sample (CP starts cp_p-1 and down)
*
* Output Parameters:
* Functions Called:
*
******************************************************************************/
PHY_status_t PHY_txCpInsert(int16 *cp_p)
{
	Uint16 i;

	/* data_p point to FFT end + 1 */
	int16 *data_p = cp_p + 256;

	for (i = 0; i < 24; i++)
	{
		*--cp_p = *--data_p;
	}

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_txSymbProc
*
* DESCRIPTION:   TX symbol processing
*
* Return Value:  None
*
* Input Parameters:
*
* Output Parameters:
* Functions Called:
*
******************************************************************************/
PHY_status_t PHY_txSymbProc(void)
{
	int16 i;
	ComplexShort *temp_p, *temp1_p;

	/* Half-band implementation */
	/* Copy the tones (startIdx~centerIdx) to near fs */
	int32 *long_p = (int32 *)PHY_tx_data_s.ifft_hnd.ipcbptr + 128 - (PHY_bandCfg_s.centerIdx - PHY_bandCfg_s.startIdx);
  
	PLC_print("[PHY_txSymbProc]: symbCnt = %d\n", PHY_tx_data_s.symbCnt);

	UTIL_blockLongCopy(PHY_bandCfg_s.centerIdx - PHY_bandCfg_s.startIdx, (int32 *)PHY_tx_data_s.modOut_p, long_p);

	long_p = (int32 *)PHY_tx_data_s.ifft_hnd.ipcbptr + (PHY_bandCfg_s.endIdx - PHY_bandCfg_s.centerIdx) + 1;

	UTIL_blockLongFill(79, long_p, 0);

    PHY_cifft16(&PHY_tx_data_s.ifft_hnd);

	PHY_txCpInsert(PHY_tx_data_s.ifft_hnd.ipcbptr);

	// new 01/25/2012 -- adding windowing
	temp_p = (ComplexShort *)PHY_tx_data_s.ifft_hnd.ipcbptr - 12;
	temp1_p = (ComplexShort *)PHY_tx_data_s.ifft_hnd.ipcbptr + 127;

	for (i = 0; i < 4; i++)
	{
		temp_p->real = ((int32)temp_p->real * (int32)PHY_txWndCoef[i] + 16384) >> 15;
		temp_p->imag = ((int32)temp_p->imag * (int32)PHY_txWndCoef[i] + 16384) >> 15;

		temp1_p->real = ((int32)temp1_p->real * (int32)PHY_txWndCoef[i] + 16384) >> 15;
		temp1_p->imag = ((int32)temp1_p->imag * (int32)PHY_txWndCoef[i] + 16384) >> 15;

		temp_p++;
		temp1_p--;
	}

	/* The following may be done by CLA later */
	/* Prefill for FIR */
	UTIL_blockLongCopy(3, (int32 *)PHY_tx_scratchBuf.buf1, (int32 *)PHY_tx_data_s.upsIn_p);

	/* Store for next symbol */
	UTIL_blockLongCopy(3, (int32 *)PHY_tx_data_s.upsIn_p + 140, (int32 *)PHY_tx_scratchBuf.buf1);

	/* Upsample x4, complex in, real FIR */
	PHY_firCplxInUp8Cvt(140, &PHY_tx_data_s.upsFir, (SINT16 *)PHY_tx_data_s.upsIn_p, (SINT16 *)PHY_tx_data_s.afeBuf_p[PHY_tx_data_s.afeBufIdx]);

	HAL_afe_txSetParms_s.txBuf_p = (int16 *)PHY_tx_data_s.afeBuf_p[PHY_tx_data_s.afeBufIdx];
	HAL_afe_txSetParms_s.txSize = PHY_tx_handle_s.txSize;

	HAL_afeSet(AFE_TX_RECFG, &HAL_afe_txSetParms_s);

	PHY_tx_data_s.afeBufIdx ^= 1;

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_txRstBufPtr()
*
* DESCRIPTION:   This function resets scratch buffer pointer for each symbol.
*
* Return Value:
*
* Input Parameters:
* Output Parameters:
*
******************************************************************************/
void PHY_txRstBufPtr()
{
	PHY_tx_data_s.bufBegin_p = (int16 *)(PHY_tx_data_s.afeBuf_p[PHY_tx_data_s.afeBufIdx] + 1128) - 286;
	PHY_tx_data_s.bufUserBegin_p = (int16 *)PHY_tx_data_s.bufBegin_p + 6;

	PHY_tx_data_s.bufEnd_p = (int16 *)(PHY_tx_data_s.afeBuf_p[PHY_tx_data_s.afeBufIdx] + 1128) - 1;

	PHY_tx_data_s.bufA_p = PHY_tx_data_s.bufUserBegin_p;
	PHY_tx_data_s.bufB_p = PHY_tx_data_s.bufA_p + 48;

	PHY_tx_data_s.ifft_hnd.ipcbptr = (int16 *)(PHY_tx_data_s.afeBuf_p[PHY_tx_data_s.afeBufIdx] + 1128) - 256;
	PHY_tx_data_s.modOut_p = PHY_tx_data_s.ifft_hnd.ipcbptr - ((PHY_bandCfg_s.centerIdx - PHY_bandCfg_s.startIdx) << 1);
	PHY_tx_data_s.upsIn_p = (int16 *)PHY_tx_data_s.bufBegin_p;
}

/******************************************************************************
* FUNCTION NAME: PHY_txRoboRepeat()
*
* DESCRIPTION:   This function combines soft bits for ROBO mode.
*
* Return Value:
*
* Input Parameters:
* Output Parameters:
*
******************************************************************************/
void PHY_txRoboRepeat(UINT16 numBlk, UINT16 blkSize, SINT16 *src_p, SINT16 *dst_p)
{
	UINT16 idx1, idx2;

	for (idx1 = 0; idx1 < blkSize; idx1++)
	{
		for (idx2 = 0; idx2 < numBlk; idx2++)
		{
			*dst_p++ = *src_p;
		}

		src_p++;
	}
}

/******************************************************************************
* FUNCTION NAME: PHY_txDataProc
*
* DESCRIPTION:   Process PHY TX data
*
* Return Value:  None
*
* Input Parameters:
*
* Output Parameters:
* Functions Called:
*
******************************************************************************/
PHY_status_t PHY_txDataProc()
{
	Uint16 i, numWordsIn, numWordsInLastSymb, numBitsInLastSymb;
	Uint16 symbCnt, numDataSymbols, numBitsInSymbol;

	PLC_print("[PHY_txDataProc]: symbCnt = %d\n", PHY_tx_data_s.symbCnt);

	PHY_txRstBufPtr();
  
	/* Encoder setup*/
	if ((PHY_tx_handle_s.roboMode == 1) && (PHY_tx_data_s.enc > 1))
	{
		/* ROBO mode, encoding 4/8 symbols (4*24//8*12=96bits) at a time */
		/* Enoder output to buf3 */
		PHY_tx_data_s.intlvOut_p =  (int16 *)PHY_tx_scratchBuf.ilvBuf; 
	}
	else
	{
		/* PRIME mode, encoding 1 symbol (96 bits) at a time */
		/* Encoder output to scratch buffer (bufB_p) */
		PHY_tx_data_s.intlvOut_p =  PHY_tx_data_s.bufEnd_p - PHY_tx_data_s.numBitsInSymbol + 1;
	}

	if (PHY_tx_data_s.enc != 0)
	{
		if (PHY_tx_data_s.symbCnt == 0)
		{
			/* First data symbol -- need to initialize encoder */
			PHY_cnvEncInit(&PHY_tx_data_s.cnvEnc);
		}

		/* unpack data */
		/* #of words=(#of coded bits in symbol)/2/16 */
		/* Do unpack & coding every 2 (non-Robo), 8 (1/4) or 16 (1/8) symbols 
			So always unpack 3 raw words */
		if ((PHY_tx_handle_s.roboMode == 1) && (PHY_tx_data_s.enc > 1))
		{
			symbCnt = PHY_tx_data_s.symbCnt >> PHY_tx_data_s.enc;
			numDataSymbols = (PHY_tx_data_s.numDataSymbols + (1 << PHY_tx_data_s.enc) - 1) >> PHY_tx_data_s.enc;
			numBitsInSymbol = PHY_tx_data_s.numBitsInSymbol << PHY_tx_data_s.enc;
		}
		else
		{
			symbCnt = PHY_tx_data_s.symbCnt;
			numDataSymbols = PHY_tx_data_s.numDataSymbols;
			numBitsInSymbol = PHY_tx_data_s.numBitsInSymbol;
		}

		if (symbCnt < numDataSymbols - 1)
		{
			/* Only do encode every 2/8/16 symbols */
			if (((PHY_tx_handle_s.roboMode == 0) && (PHY_tx_data_s.symbCnt & 1) == 0) ||
				((PHY_tx_handle_s.roboMode == 1) && (PHY_tx_data_s.enc == 1) &&
				((PHY_tx_data_s.symbCnt & 1) == 0))||
				((PHY_tx_handle_s.roboMode == 1) && (PHY_tx_data_s.enc > 1) &&
				((PHY_tx_data_s.symbCnt & ((1 << (PHY_tx_data_s.enc + 1)) - 1)) == 0)))
			{
				/* Check if it's the last block as one-shot (3W) has 2 symbols */
				if (symbCnt == numDataSymbols - 2)
				{
					int16 *temp_p;

					numBitsInLastSymb = PHY_tx_data_s.numInBits - (numDataSymbols - 1) * (numBitsInSymbol >> 1);
					numWordsInLastSymb = (numBitsInLastSymb + 15) >> 4;

					PHY_tx_data_s.numBitsInLastSymb = numBitsInLastSymb;
					PHY_tx_data_s.numWordsInLastSymb = numWordsInLastSymb;
      
					/* Unpack 48 bits (3W) to buf2, encode to buf3 - can be less */
					UTIL_blockUnpack2Bits((numBitsInLastSymb + (numBitsInSymbol >> 1) + 15) >> 4, (Uint16 *)PHY_tx_data_s.ppduPld_p + (numBitsInSymbol >> 4) * (symbCnt >> 1), (Uint16 *)PHY_tx_scratchBuf.buf3);
          
					/* Filling the flushing bits */
					/* Starting offset of the flush byte is (1 or 0)*RawBitsInSymbol(24) */
					/* Note this is the case symbCnt even */
					temp_p = (int16 *)PHY_tx_scratchBuf.buf3 + numBitsInLastSymb + (numBitsInSymbol >> 1);
					
					for (i = 0; i < 8; i++)
					{
						*temp_p++ = 0;
					}
          
					PHY_tx_data_s.cnvEnc.nBits = (numBitsInSymbol >> 1) + numBitsInLastSymb + 8;

					PHY_cnvEnc(&PHY_tx_data_s.cnvEnc);

					/* Zero out the padding bits -- not needed */
					temp_p = (int16 *)PHY_tx_scratchBuf.buf2 + numBitsInSymbol + numBitsInLastSymb * 2 + 8 * 2;

					for (i = 0; i < (PHY_tx_data_s.numPadBits << 1); i++)
					{
						*temp_p++ = 0;
					}
				}
				else
				{
					/* Unpack 48 bits (3W) to buf2, encode to buf3, every 2/8/16 symbols */
					UTIL_blockUnpack2Bits(3, (Uint16 *)PHY_tx_data_s.ppduPld_p + (numBitsInSymbol >> 4) * (symbCnt >> 1), (Uint16 *)PHY_tx_scratchBuf.buf3);

					PHY_tx_data_s.cnvEnc.nBits = numBitsInSymbol;
			
					PHY_cnvEnc(&PHY_tx_data_s.cnvEnc);
				}
			}
		}
		else
		{
			/* Last symbol - this only happens when numDataSymbol is odd (symbCnt even) */
			/* up to 3 words (48 bits) * (mod + 1) is the number of regular input bits in 2/8/16 symbols */
			/* if symbCnt is odd, then it's already encoded in buf3 */
			if (((PHY_tx_handle_s.roboMode == 0) && (PHY_tx_data_s.symbCnt & 1) == 0) ||
				((PHY_tx_handle_s.roboMode == 1) && (PHY_tx_data_s.enc == 1) &&
				((PHY_tx_data_s.symbCnt & 1) == 0))||
				((PHY_tx_handle_s.roboMode == 1) && (PHY_tx_data_s.enc > 1) &&
				((PHY_tx_data_s.symbCnt & ((1 << (PHY_tx_data_s.enc + 1)) - 1)) == 0)))
			{
				int16 *temp_p;

				numBitsInLastSymb = PHY_tx_data_s.numInBits - (numDataSymbols - 1) * (numBitsInSymbol >> 1);
				numWordsInLastSymb = (numBitsInLastSymb + 15) >> 4;

				PHY_tx_data_s.numBitsInLastSymb = numBitsInLastSymb;
				PHY_tx_data_s.numWordsInLastSymb = numWordsInLastSymb;

				UTIL_blockUnpack2Bits(numWordsInLastSymb, (Uint16 *)PHY_tx_data_s.ppduPld_p + (numBitsInSymbol >> 4) * (symbCnt >> 1), (Uint16 *)PHY_tx_scratchBuf.buf3);

				/* Filling the flushing bits */
				temp_p = (int16 *)PHY_tx_scratchBuf.buf3 + numBitsInLastSymb;
				
				for (i = 0; i < 8; i++)
				{
					*temp_p++ = 0;
				}

				PHY_tx_data_s.cnvEnc.nBits = numBitsInLastSymb + 8;

				PHY_cnvEnc(&PHY_tx_data_s.cnvEnc);

				/* Zero out the padding bits -- not needed */
				temp_p = (int16 *)PHY_tx_scratchBuf.buf2 + numBitsInLastSymb * 2 + 8 * 2;

				for (i = 0; i < (PHY_tx_data_s.numPadBits << 1); i++)
				{
					*temp_p++ = 0;
				}
			}
		}

		if ((PHY_tx_handle_s.roboMode == 1) && (PHY_tx_data_s.enc > 1))
		{
			if ((PHY_tx_data_s.symbCnt & 3) == 0)
			{
				/* ROBO for Data */
				PHY_txRoboRepeat((1 << PHY_tx_data_s.enc), PHY_tx_data_s.numBitsInSymbol * 4, (SINT16 *)PHY_tx_scratchBuf.buf2 + PHY_tx_data_s.numBitsInSymbol * (PHY_tx_data_s.symbCnt & ((1 << (PHY_tx_data_s.enc + 1)) - 1)), (SINT16 *)PHY_tx_data_s.bufB_p);
        
				PHY_scm((Uint16 *)PHY_tx_data_s.bufB_p, (Uint16 *)PHY_tx_data_s.bufA_p, numBitsInSymbol * 4);

				PHY_intlvBlk(numBitsInSymbol * 4, (Uint16 *)PHY_tx_data_s.bufA_p, (Uint16 *)PHY_tx_data_s.intlvOut_p, 1);
			}

			PHY_mod_genPld((Uint16 *)PHY_tx_data_s.intlvOut_p + (PHY_tx_data_s.symbCnt & 3) * numBitsInSymbol, (ComplexShort *)PHY_tx_data_s.modOut_p, PHY_tx_data_s.mod);
		}
		else
		{
			PHY_scm((Uint16 *)PHY_tx_scratchBuf.buf2 + PHY_tx_data_s.numBitsInSymbol * (PHY_tx_data_s.symbCnt & 1), (Uint16 *)PHY_tx_data_s.bufA_p, numBitsInSymbol);
  
			PHY_intlv(numBitsInSymbol, (Uint16 *)PHY_tx_data_s.bufA_p, (Uint16 *)PHY_tx_data_s.intlvOut_p, 1);

			PHY_mod_genPld((Uint16 *)PHY_tx_data_s.intlvOut_p, (ComplexShort *)PHY_tx_data_s.modOut_p, PHY_tx_data_s.mod);
		}
	}
	else
	{
		/* Uncoded - need to parse PPDU data to 1bit/word and store in bufB_p */
		/* #of words=(#of coded bits in symbol)/16 */
		numWordsIn =  PHY_tx_data_s.numBitsInSymbol >> 4;
	  
		UTIL_blockUnpack2Bits(numWordsIn, (Uint16 *)PHY_tx_data_s.ppduPld_p + numWordsIn * PHY_tx_data_s.symbCnt, (Uint16 *)PHY_tx_data_s.bufA_p);
  
		/* For uncoded case, no interleaver, directly put the scrambler output in the interleaver buffer for modulator */
		PHY_scm((Uint16 *)PHY_tx_data_s.bufA_p, (Uint16 *)PHY_tx_data_s.intlvOut_p, PHY_tx_data_s.numBitsInSymbol);

		PHY_mod_genPld((Uint16 *)PHY_tx_data_s.intlvOut_p, (ComplexShort *)PHY_tx_data_s.modOut_p, PHY_tx_data_s.mod);
	}

	PHY_txSymbProc();

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_tx_dataProc
*
* DESCRIPTION:   This function processes PPDU data symbols.
*
* Return Value:
*
* Input Parameters:
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_tx_dataProc(void *data_p)
{
	PLC_print("[PHY_tx_dataProc]: symbCnt = %d\n", PHY_tx_data_s.symbCnt);

	if (PHY_tx_data_s.symbCnt < PHY_tx_data_s.numDataSymbols)
	{
		PHY_txDataProc();

		PHY_tx_data_s.symbCnt++;
	}
	else
	{
		FSM_event(data_p, PHY_TX_EVT_RETRY, (void *)data_p);
	}

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_tx_dataStart
*
* DESCRIPTION:   This function starts PPDU data processing
*
* Return Value:
*
* Input Parameters:
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_tx_dataStart(void *data_p)
{
	PLC_print("[PHY_tx_dataStart]: symbCnt = %d\n", PHY_tx_data_s.symbCnt);

	PHY_tx_data_s.timeLog.dataStartTime = 10;

	PHY_tx_data_s.symbCnt = 0;

	/* Encoder setup*/
	// Half-band: always 3W
	if (PHY_tx_data_s.enc != 0)
	{
		PHY_tx_data_s.cnvEnc.u_p = (Uint16 *)PHY_tx_scratchBuf.buf3;
		PHY_tx_data_s.cnvEnc.c_p = (Uint16 *)PHY_tx_scratchBuf.buf2;
		PHY_tx_data_s.cnvEnc.nBits = 48;
	}

	PHY_txDataProc();

	PHY_tx_data_s.symbCnt++;

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_txPpduHdr
*
* DESCRIPTION:   Generate the TX PPDU header (84 bits)
*
* Return Value:  None
*
* Input Parameters:
*
* Output Parameters:
* Functions Called:
*
******************************************************************************/
PHY_status_t PHY_txPpduHdr(Uint16 *pBuf, Uint16 protocol, Uint16 len, Uint16 padLen)
{
	uint16  crc;
	uint8   *pdata = (uint8 *)pBuf;

	PLC_print("[PHY_txPpduHdr]: protocol = %d, %d, %d\n", protocol, len, padLen);

	// Pack the first 24 bits
	// new: protocol | len | padlen = 4 | 12 | 8
	pdata[0] = ((protocol << 4) | (len >> 8)) & 0xFF;
	pdata[1] = (len & 0xFF);
	pdata[2] = (padLen & 0xFF);

	// Calculate the CRC8
	crc = getCRC8(INIT_CRC8, pBuf, EVEN, 3);

	// Add the crc
	pdata[3] = (uint8)crc;

	// Zero the rest (including flush bits)
	pBuf[2] = 0;

	return PHY_STAT_SUCCESS;
}


/******************************************************************************
* FUNCTION NAME: PHY_txHdrBitProc
*
* DESCRIPTION:   PHY TX header bit processing
*
* Return Value:  None
*
* Input Parameters:
*
* Output Parameters:
* Functions Called:
*
******************************************************************************/
PHY_status_t PHY_txHdrBitProc()
{
	Uint16 *buf_p;
	Uint16 tempHdrBuf[3] = {0};

	/* 2 symbols: 2x42=84 bits; raw 42 bits or 3 words */
	PLC_print("[PHY_txHdrBitProc]: symbCnt = 0x%x\n", PHY_tx_data_s.symbCnt);

	PHY_txRstBufPtr();

	PHY_tx_data_s.intlvOut_p =  (int16 *)PHY_tx_scratchBuf.ilvBuf;  

	if (PHY_tx_data_s.symbCnt == 0)
	{
		/* First header symbol */
		/* convolutional encode */
		PHY_cnvEncInit(&PHY_tx_data_s.cnvEnc);

		buf_p = tempHdrBuf;

		PHY_txPpduHdr(buf_p, (PHY_tx_data_s.enc << 2) | PHY_tx_data_s.mod, PHY_tx_data_s.numDataSymbols, PHY_tx_data_s.numPadBits);

		/* Unpack data - one shot for two header symbole */
		/* Unpack to buf2, from 3 words to 48 (3*16) words with 1bit/word */
		/* Actual data should only occupy 42 bits (42 words for 1bit/word) */
		UTIL_blockUnpack2Bits(3, buf_p, PHY_tx_scratchBuf.buf2);
	}

	if (PHY_tx_handle_s.roboMode == 0)
	{
		/* PRIME mode */
		PHY_tx_data_s.cnvEnc.u_p  = PHY_tx_scratchBuf.buf2 + PHY_tx_data_s.cnvEnc.nBits * PHY_tx_data_s.symbCnt;
		PHY_tx_data_s.cnvEnc.c_p = (Uint16 *)PHY_tx_data_s.bufB_p;
		PHY_tx_data_s.cnvEnc.nBits = 21;

		PHY_cnvEnc(&PHY_tx_data_s.cnvEnc);
  
		PHY_scm((Uint16 *)PHY_tx_data_s.bufB_p, (Uint16 *)PHY_tx_data_s.bufA_p, 42);

		/* Note header is always DBPSK or 1bit/symbol */
		PHY_intlv(42, (Uint16 *)PHY_tx_data_s.bufA_p, (Uint16 *)PHY_tx_data_s.intlvOut_p, 0);

		PHY_mod_genHdr((Uint16 *)PHY_tx_data_s.intlvOut_p, (ComplexShort *)PHY_tx_data_s.modOut_p);
	}
	else
	{
		/* ROBO mode, header always 1/8, do encoder all in one shot, result (84) in buf3 (96) */
		if (PHY_tx_data_s.symbCnt == 0)
		{
			PHY_tx_data_s.cnvEnc.nBits = 42; //actually only 30 bits valid data
			PHY_tx_data_s.cnvEnc.u_p  = PHY_tx_scratchBuf.buf2;
			PHY_tx_data_s.cnvEnc.c_p  = PHY_tx_scratchBuf.buf3;
			
			PHY_cnvEnc(&PHY_tx_data_s.cnvEnc);
		}

		/* Repetitive: out of the 84 coded bits, x8=672, split to 16 symbols
			Do 4 symbols (21x8->168 bits) at a time, store in Buf2
		*/
		if ((PHY_tx_data_s.symbCnt & 3) == 0)
		{
			/* Repeat the 21 bits 8 times into buf2, for 4 symbols */
			PHY_txRoboRepeat(8, 21, (SINT16 *)PHY_tx_scratchBuf.buf3 + 21 * (PHY_tx_data_s.symbCnt >> 2), (SINT16 *)PHY_tx_scratchBuf.buf2);

			PHY_scm((Uint16 *)PHY_tx_scratchBuf.buf2, (Uint16 *)PHY_tx_data_s.bufA_p, 42 * 4);

			/* Note header is always DBPSK or 1bit/symbol */
			PHY_intlvBlk(42 * 4, (Uint16 *)PHY_tx_data_s.bufA_p, (Uint16 *)PHY_tx_data_s.intlvOut_p, 0);
		}
  
		PHY_mod_genHdr((Uint16 *)PHY_tx_data_s.intlvOut_p + 42 * (PHY_tx_data_s.symbCnt & 3), (ComplexShort *)PHY_tx_data_s.modOut_p);
	}

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_txHdrProc
*
* DESCRIPTION:   Process PHY TX header
*
* Return Value:  None
*
* Input Parameters:
*
* Output Parameters:
* Functions Called:
*
******************************************************************************/
PHY_status_t PHY_txHdrProc()
{
	PLC_print("[PHY_txHdrProc]: symbCnt = %d\n", PHY_tx_data_s.symbCnt);
	
	PHY_txHdrBitProc();

	PHY_txSymbProc();

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_tx_hdrProc
*
* DESCRIPTION:   This function processes the second PPDU header symbol, and transitions
*                to data processing once finish
*
* Return Value:
*
* Input Parameters:
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_tx_hdrProc(void *data_p)
{
	PLC_print("[PHY_tx_hdrProc]: symbCnt = %d\n", PHY_tx_data_s.symbCnt);

	if (PHY_tx_data_s.symbCnt < PHY_tx_data_s.numHdrSymbols)
	{
		PHY_txHdrProc();
		
		PHY_tx_data_s.symbCnt++;
	}
	else
	{
		PHY_tx_data_s.symbCnt = 0;

		FSM_event(data_p, PHY_TX_EVT_DONE, (void *)data_p);
	}

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_tx_hdrStart
*
* DESCRIPTION:   This function starts PPDU header processing
*                This is also the time when the first sample in the preamble going out
*
* Return Value:
*
* Input Parameters:
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_tx_hdrStart(void *data_p)
{
	PLC_print("[PHY_tx_hdrStart]: symbCnt = %d\n", PHY_tx_data_s.symbCnt);

	PHY_tx_data_s.timeLog.hdrStartTime = 10;

	if (PHY_tx_data_s.timeLog.ppduTime > PHY_tx_data_s.timeLog.hdrStartTime)
	{
		PHY_tx_data_s.stats.phyTxProcDelay = PHY_tx_data_s.timeLog.ppduTime - PHY_tx_data_s.timeLog.hdrStartTime;
	}
	else
	{
		PHY_tx_data_s.stats.phyTxProcDelay = PHY_tx_data_s.timeLog.ppduTime + 0xFFFFF - PHY_tx_data_s.timeLog.hdrStartTime;
	}

	PHY_tx_data_s.symbCnt = 0;
	PHY_tx_data_s.numSamps = 1120;

	PHY_tx_handle_s.txSize = 1 * 1120;
	PHY_tx_data_s.afeBufIdx = 0;

	PHY_txRstBufPtr();

	UTIL_blockLongCopy(49, (SINT32 *)PHY_tx_scratchBuf.buf4, (SINT32 *)PHY_tx_data_s.modOut_p);

	/* Here need to call a function to fill the header, or make sure MAC has filled it */
	PHY_txSymbProc();

	PHY_tx_data_s.symbCnt++;

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_tx_syncmProc
*
* DESCRIPTION:   This function processes syncm
*
* Return Value:
*
* Input Parameters:
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_tx_syncmProc(void *data_p)
{
	PLC_print("[PHY_tx_syncmProc]: symbCnt = %d\n", PHY_tx_data_s.symbCnt);

	PHY_tx_data_s.symbCnt++;

	if (PHY_tx_data_s.symbCnt >= 1)
	{
		FSM_event(data_p, PHY_TX_EVT_DONE, (void *)data_p);
	}

	return PHY_STAT_SUCCESS;
}


/******************************************************************************
* FUNCTION NAME: PHY_txSyncmProc
*
* DESCRIPTION:   Process PHY TX preamble
*
* Return Value:  None
*
* Input Parameters:
*
* Output Parameters:
* Functions Called:
*
******************************************************************************/
void PHY_txSyncmProc()
{
	SINT16 kk, *tmp_p, *tmp1_p;
	PHY_cfft16_t *ifft_hnd;

	ifft_hnd = &PHY_tx_data_s.ifft_prmb_hnd;

	PHY_tx_data_s.upsIn_p = (int16 *)ifft_hnd->ipcbptr - 6;

	/* Note for SYNCM, IFFT is still in afeBuf[1], upsampling to afeBuf[1] */

	/* Only do upsampling for the first prefill samples; the rest from -SYNCP */
	tmp_p = (SINT16 *)ifft_hnd->ipcbptr;

	for (kk = 0; kk < 6; kk++)
	{
		*tmp_p = - *tmp_p;

		tmp_p++;
	}

	/* Now copy the last samples into buf1, for use in the next symbol (header 1) *
	 * need to negate as this is SYNCM while the ifft buffer still holds SYNCP */
	tmp_p = (SINT16 *)ifft_hnd->ipcbptr + 250;
	tmp1_p = (SINT16 *)PHY_tx_scratchBuf.buf1;

	for (kk = 0; kk < 6; kk++)
	{
		*tmp1_p++ = - *tmp_p++;
	}
  
	/* Upsample, filter, and upconvert */
	PHY_firCplxInUp8Cvt(3, &PHY_tx_data_s.upsFir, (SINT16 *)PHY_tx_data_s.upsIn_p, (SINT16 *)PHY_tx_data_s.afeBuf_p[PHY_tx_data_s.afeBufIdx]);

	/* Copy the rest from -SYNCP */
	tmp_p = (SINT16 *)PHY_tx_data_s.afeBuf_p[PHY_tx_data_s.afeBufIdx^1] + 24;
	tmp1_p = (SINT16 *)PHY_tx_data_s.afeBuf_p[PHY_tx_data_s.afeBufIdx] + 24;

	for (kk = 24; kk < 1024; kk++)
	{
		*tmp1_p++ = - *tmp_p++;
	}

	/* Set HAL AFE buffer to afeBuf[1] */
	HAL_afe_txSetParms_s.txBuf_p = (int16 *)PHY_tx_data_s.afeBuf_p[PHY_tx_data_s.afeBufIdx];
	HAL_afe_txSetParms_s.txSize = PHY_tx_handle_s.txSize;

	HAL_afeSet(AFE_TX_RECFG, &HAL_afe_txSetParms_s);
}

/******************************************************************************
* FUNCTION NAME: PHY_tx_syncmStart
*
* DESCRIPTION:   This function processes syncm
*
* Return Value:
*
* Input Parameters:
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_tx_syncmStart(void *data_p)
{
	PLC_print("[PHY_tx_syncmStart]: symbCnt = %d\n", PHY_tx_data_s.symbCnt);

	PHY_tx_data_s.symbCnt = 0;
  
	/* Split the header proc for MIPS purpose - do header bits here */
	PHY_tx_data_s.afeBufIdx = 1;

	PHY_txHdrBitProc();

	UTIL_blockLongCopy(49, (SINT32 *)PHY_tx_data_s.modOut_p, (SINT32 *)PHY_tx_scratchBuf.buf4);

	PHY_txSyncmProc();

	return PHY_STAT_SUCCESS;
}

/******************************************************************************
* FUNCTION NAME: PHY_tx_syncpProc
*
* DESCRIPTION:   This function processes syncp
*
* Return Value:
*
* Input Parameters:
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_tx_syncpProc(void *data_p)
{
	UINT16 numSynCp;

	PLC_print("[PHY_tx_syncpProc]: symbCnt = %d\n", PHY_tx_data_s.symbCnt);

	PHY_tx_data_s.symbCnt++;

	if (PHY_tx_handle_s.roboMode == 0)
	{
		numSynCp = 3;
	}
	else
	{
		numSynCp = 6;
	}

	if (PHY_tx_data_s.symbCnt == numSynCp - 1)
	{
		FSM_event(data_p, PHY_TX_EVT_DONE, (void *)data_p);
	}

	return PHY_STAT_SUCCESS;
}


/******************************************************************************
* FUNCTION NAME: PHY_txPrmbProc
*
* DESCRIPTION:   Process PHY TX preamble
*
* Return Value:  None
*
* Input Parameters:
*
* Output Parameters:
* Functions Called:
*
******************************************************************************/
void PHY_txSyncpProc(UINT16 scaleFlag)
{
	int32 *long_p;
	PHY_cfft16_t *ifft_hnd;

	PLC_print("[PHY_txSyncpProc]: symbCnt = %d\n", PHY_tx_data_s.symbCnt);

	ifft_hnd = &PHY_tx_data_s.ifft_prmb_hnd;
	ifft_hnd->ipcbptr = &PHY_tx_prmb_workBuf[6];

	PHY_tx_data_s.upsIn_p = (int16 *)ifft_hnd->ipcbptr - 6;

	/* Copy the tones (startIdx~centerIdx) to near fs */
	long_p = (int32 *)ifft_hnd->ipcbptr + 128 - (PHY_bandCfg_s.centerIdx - PHY_bandCfg_s.startIdx);

	UTIL_blockLongFill(79, (int32 *)ifft_hnd->ipcbptr + PHY_bandCfg_s.numHiTones, 0);

	UTIL_blockLongCopy(PHY_bandCfg_s.numLoTones, (int32 *)PHY_prmb_freq, long_p);

	UTIL_blockLongCopy(PHY_bandCfg_s.numHiTones, (int32 *)PHY_prmb_freq + PHY_bandCfg_s.numLoTones, (int32 *)ifft_hnd->ipcbptr);

	/* IFFT */
	PHY_cifft16(ifft_hnd);
  
	if (scaleFlag == 1)
	{
		int kk;

		/* Put a variable scale here, to boost the prmb */
		SINT16 *x_p = (SINT16 *)ifft_hnd->ipcbptr;
		SINT16 scale = PHY_tx_data_s.prmbScale;

		for (kk = 0; kk < 256; kk++)
		{
			SINT16 temp;

			/* scale is Q13 */
			temp = (SINT16)(((SINT32)*x_p * (SINT32)scale) >> 13);

			*x_p = temp;
			x_p++;
		}
	}

	/* Copy the tail to the prefill - only needed for the second preamble */
	UTIL_blockLongCopy(3, (int32 *)ifft_hnd->ipcbptr + 125, (int32 *)PHY_tx_data_s.upsIn_p);

	/* Upsample, filter, and upconvert */
	PHY_firCplxInUp8Cvt(128, &PHY_tx_data_s.upsFir, (SINT16 *)PHY_tx_data_s.upsIn_p, (SINT16 *)PHY_tx_data_s.afeBuf_p[PHY_tx_data_s.afeBufIdx]);
}

/******************************************************************************
* FUNCTION NAME: PHY_tx_syncpStart
*
* DESCRIPTION:   This function starts preamble processing
*                Upon filling the output buffer, it starts TX
*                Latency is mainly the upsampling filter (~500us)
*
* Return Value:
*
* Input Parameters:
*
* Output Parameters:
*
******************************************************************************/
PHY_status_t PHY_tx_syncpStart(void *data_p)
{
	PHY_tx_data_s.timeLog.preStartTime = 10;

	PLC_print("[PHY_tx_syncpStart]: symbCnt = %d\n", PHY_tx_data_s.symbCnt);

	PHY_tx_data_s.afeBufIdx = 0;
	
	PHY_txSyncpProc(1);

	/* Configure timer0 */
	HAL_afe_txSetParms_s.t0PrdInUs = 4;
	HAL_afe_txSetParms_s.t0Flags = HAL_AFE_TIMER_INT_ENABLE;
	HAL_afe_txSetParms_s.cb_p    = NULL;
  
	HAL_afeSet(AFE_TX_T0CFG, &HAL_afe_txSetParms_s);

	/* Start DEV TX */
	HAL_afeSet(AFE_TX_START, &HAL_afe_txSetParms_s);

	PHY_tx_data_s.symbCnt = 0;

	return PHY_STAT_SUCCESS;
}


